import { app } from "../../app";
import { menu } from "../../decorator";
import Socket from "../../lib/socket/socket";

const { ccclass, property } = cc._decorator;

interface opt {
    binaryType?: BinaryType,
    connectTimeOut?: number,
    useAutoReconnect?: boolean,
    autoReconnectDelay?: number,
    useHeartbeat?: boolean,
    heartbeatDelay?: number,
    heartbeatTimeOut?: number,
    encode(event: string, data?: any, other?: any): string | ArrayBuffer,
    decode(param: string | ArrayBuffer): { event: string, data?: any }
}

@ccclass
@menu(__filename)
export default class netSocket extends cc.Component {
    @property({ tooltip: '网址' })
    url: string = '';

    @property
    private _useAutoReconnect: boolean = true
    @property({ tooltip: '是否开启自动重连' })
    get useAutoReconnect() { return this._useAutoReconnect; }
    set useAutoReconnect(value) {
        this._useAutoReconnect = value;
        if (this.socket) this.socket.useAutoReconnect = value;
    }

    @property
    private _useHeartbeat: boolean = false
    @property({ tooltip: '是否启用心跳' })
    get useHeartbeat() { return this._useHeartbeat; }
    set useHeartbeat(value) {
        this._useHeartbeat = value;
        if (this.socket) this.socket.useHeartbeat = value;
    }

    @property
    private _connectTimeOut: number = 2000
    @property({ tooltip: '连接超时时间(毫秒)' })
    get connectTimeOut() { return this._connectTimeOut; }
    set connectTimeOut(value) {
        this._connectTimeOut = value;
        if (this.socket) this.socket.connectTimeOut = value;
    }

    @property
    private _autoReconnectDelay: number = 2000
    @property({ tooltip: '重连等待时间(毫秒)', visible(this: netSocket) { return this._useAutoReconnect; } })
    get autoReconnectDelay() { return this._autoReconnectDelay; }
    set autoReconnectDelay(value) {
        this._autoReconnectDelay = value;
        if (this.socket) this.socket.autoReconnectDelay = value;
    }

    @property
    private _heartbeatDelay: number = 2000
    @property({ tooltip: '心跳等待时间(毫秒)', visible(this: netSocket) { return this._useHeartbeat; } })
    get heartbeatDelay() { return this._heartbeatDelay; }
    set heartbeatDelay(value) {
        this._heartbeatDelay = value;
        if (this.socket) this.socket.heartbeatDelay = value;
    }

    @property
    private _heartbeatTimeOut: number = 2000
    @property({ tooltip: '心跳超时时间(毫秒)', visible(this: netSocket) { return this._useHeartbeat; } })
    get heartbeatTimeOut() { return this._heartbeatTimeOut; }
    set heartbeatTimeOut(value) {
        this._heartbeatTimeOut = value;
        if (this.socket) this.socket.heartbeatTimeOut = value;
    }

    @property
    private _encode: cc.Component.EventHandler = null;
    @property({ tooltip: '编码', visible(this: netSocket) { return this._useHeartbeat; } })
    get encode() { return this._encode; }
    set encode(value) {
        this._encode = value;
        if (this.socket) this.socket.encode = function (event: string, data?: any, other?: any): string | ArrayBuffer {
            const component = value.target.getComponent(value.component);
            return component[value.handler].call(component, event, data, other);
        };
    }

    @property
    private _decode: cc.Component.EventHandler = null;
    @property({ tooltip: '编码', visible(this: netSocket) { return this._useHeartbeat; } })
    get decode() { return this._decode; }
    set decode(value) {
        this._encode = value;
        if (this.socket) this.socket.decode = function (param: string | ArrayBuffer): { event: string, data?: any } {
            const component = value.target.getComponent(value.component);
            return component[value.handler].call(component, param);
        };
    }

    private socket: Socket = null;
    private results = [];

    public connect(opt?: opt) {
        if (!this.socket) {
            this.socket = app.lib.socket.create(this.url, opt);
            this.socket.on('message', function (this: netSocket, result) {
                this.results.push(result);
            }, this);
        }

        if (this.state !== WebSocket.CLOSED) return false;

        this.socket.connect(this.useAutoReconnect, this.useHeartbeat);
        return true;
    }

    public close() {
        if (!this.socket) return false;
        this.socket.close();
        return true;
    }

    public abort() {
        if (!this.socket) return false;
        this.results.length = 0;
        this.socket.off();
        this.socket.close();
        this.socket = null;
        return true;
    }

    public get state() {
        if (!this.socket) return WebSocket.CLOSED;
        return this.socket.state;
    }

    public send(event: string, data?: any, other?: any) {
        if (!this.socket) return false;
        this.socket.send(event, data, other);
        return true;
    }

    onDestroy() {
        this.abort();
    }

    update() {
        if (this.results.length) {
            const result = this.results.shift();
            this.node.emit(result.event, result.data);
        }
    }
}
